package evs.location;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;

public class SearchRequestHandler extends AbstractRequestHandler
{
    public SearchRequestHandler( HttpServletRequest request, SessionFactory sessionFactory,
                    List<Class<?>> interceptorClasses, EntityMap entityMap ) throws EvsException
    {
        super( request, sessionFactory, interceptorClasses, entityMap );
        
    }

    @Override
    protected Class<?> _getExpectedEntityClass()
    {
        String requestPath = extractPath( _request );
        
        int beginOfEntityName = requestPath.indexOf( '/' ) + 1;
        int endOfEntityName = requestPath.indexOf( '/', beginOfEntityName );
        
        String entityName = endOfEntityName < 0 ?
            requestPath.substring( beginOfEntityName ) :
            requestPath.substring( beginOfEntityName, endOfEntityName );
        
        return _entityMap.getEntityForPath( entityName );
    }
    
    private ArrayList<Criterion> _getRestrictions( Class<?> entityClass ) 
        throws EvsException
    {
        ArrayList<Criterion> crits = new ArrayList<Criterion>();
        Enumeration<String> parameterNames = _request.getParameterNames();
        
        while ( parameterNames.hasMoreElements() )
        {
            String name = parameterNames.nextElement();
            Field field = EntityMap.getField( entityClass, name );
            
            if ( field == null )
                continue;
            
            String valueString = _request.getParameter( name );

            Object value = Converter.convertTo( valueString, field.getType() );
             
            crits.add( Restrictions.eq( name, value ) );
        }
        
        return crits;
    }
    
    @Override
    protected boolean allowUnknownMediaType() { return false; }

    @Override
    protected void _execute( HttpServletResponse response ) throws EvsException
    {
        Session session = null;
        Transaction tx = null;
        
        try
        {
            session = _sessionFactory.openSession();
            tx = session.beginTransaction();
            
            Class<?> entityClass = _getExpectedEntityClass();
            
            ArrayList<Criterion> crits = _getRestrictions( entityClass );
            
            if ( crits.size() == 0 )
                throw new EvsException( HttpServletResponse.SC_BAD_REQUEST, "Invalid search criteria given" );
            
            Criteria criteria = session.createCriteria( entityClass );
                        
            for ( Criterion c : crits )
                criteria.add( c );
            
            response.getWriter().write( "<results>\n" );
            
            for ( Object result : criteria.list() )
            {
                EntitySerializer serializer = new EntitySerializer( _entityMap );
                EntityDom dom = serializer.serialize( result );
                
                if ( _mediaType == MediaType.XML )
                {
                    response.getWriter().write( dom.toXML() );
                }
                else
                {
                    response.getWriter().write( dom.toJson() );
                }
            }
            
            response.getWriter().write( "</results>" );
            
            tx.commit();
            tx = null;
        }
        catch ( HibernateException e )
        {
            e.printStackTrace();
            throw new EvsException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            "Hibernate error: " + e.getMessage() );
        }
        catch ( IOException e )
        {
            throw new EvsException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                            e.getMessage() );
        }
        finally
        {
            if ( tx!= null )
            {
                tx.rollback();
                tx = null;
            }
            
            if ( session != null )
            {
                session.close();
            }
        }

    }

}
